package com.example.service;

import com.example.entity.Diagnosis;
import com.example.entity.MriImages;
import com.example.mapper.DiagnosisMapper;

import com.example.mapper.MriImagesMapper;
import com.example.utils.RedisCache;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 诊断服务层
 * 提供诊断相关的业务逻辑处理，包括CRUD操作、分页查询、缓存管理等
 */
@Service
public class DiagnosisService {
    // 日志记录器
    private static final Logger log = LoggerFactory.getLogger(DiagnosisService.class);

    // 依赖注入
    @Autowired
    private DiagnosisMapper diagnosisMapper;
    @Autowired
    private MriImagesMapper mriImagesMapper;

    @Autowired
    private RedisCache redisCache;

    // Redis缓存键前缀定义
    private static final String CACHE_KEY_ID = "diagnosis:id:";          // 单个诊断缓存键
    private static final String CACHE_KEY_ALL = "diagnosis:all";         // 所有诊断列表缓存键
    private static final String CACHE_KEY_USER = "diagnosis:user:";      // 用户视图缓存键
    private static final String CACHE_KEY_EXPERT = "diagnosis:expert:";  // 专家视图缓存键

    // 缓存过期时间配置
    private static final int CACHE_EXPIRE_MINUTES = 30;                 // 正常缓存过期时间
    private static final int CACHE_EXPIRE_SHORT_MINUTES = 5;           // 空值缓存过期时间
    private static final TimeUnit CACHE_EXPIRE_UNIT = TimeUnit.MINUTES; // 过期时间单位

    private static final String STATUS_APPROVED = "Approved";
    private static final String STATUS_REFUSED = "Refused";
    private static final String STATUS_DIAGNOSING = "Diagnosing";
    private static final String STATUS_COMPLETED = "Completed";

    /**
     * 新增诊断记录
     * @param diagnosis 诊断实体
     * @throws RuntimeException 新增失败时抛出异常
     */
    @Transactional(rollbackFor = Exception.class)
    public void add(Diagnosis diagnosis) {
        try {
            // 更新 MRI 图像状态为 Pending
            MriImages mriImages = mriImagesMapper.selectById(diagnosis.getImageId());
            mriImages.setStatus("Pending");
            mriImagesMapper.updateById(mriImages);

            // 插入诊断记录
            diagnosisMapper.insert(diagnosis);
            // 清除相关缓存
            clearCache(diagnosis.getId());
        } catch (Exception e) {
            log.error("Add diagnosis failed", e);
            throw new RuntimeException("新增诊断失败", e);
        }
    }

    /**
     * 根据ID删除诊断记录
     * @param id 诊断ID
     * @throws RuntimeException 删除失败时抛出异常
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteById(Integer id) {
        try {
            diagnosisMapper.deleteById(id);
            // 清除相关缓存
            clearCache(id);
        } catch (Exception e) {
            log.error("Delete diagnosis failed", e);
            throw new RuntimeException("删除诊断失败", e);
        }
    }

    /**
     * 批量删除诊断记录
     * @param ids 诊断ID列表
     * @throws RuntimeException 批量删除失败时抛出异常
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteBatch(List<Integer> ids) {
        try {
            // 清除相关缓存
            for (Integer id : ids) {
                diagnosisMapper.deleteById(id);
                clearCache(id);
            }
        } catch (Exception e) {
            log.error("Batch delete diagnosis failed", e);
            throw new RuntimeException("批量删除诊断失败", e);
        }
    }

    /**
     * 更新诊断记录
     * @param diagnosis 诊断实体
     * @throws RuntimeException 更新失败时抛出异常
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateById(Diagnosis diagnosis) {
        try {
            diagnosisMapper.updateById(diagnosis);
            // 清除相关缓存
            clearCache(diagnosis.getId());
        } catch (Exception e) {
            log.error("Update diagnosis failed", e);
            throw new RuntimeException("更新诊断失败", e);
        }
    }

    /**
     * 更新诊断状态
     * 根据不同的状态值进行相应的状态流转
     * @param diagnosis 包含状态信息的诊断实体
     * @throws RuntimeException 更新状态失败时抛出异常
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateUpdateStatusById(Diagnosis diagnosis) {
        try {
            // 根据不同状态进行相应的状态流转
            if (STATUS_APPROVED.equals(diagnosis.getStatus())) {
                // 如果是已批准状态，则更新为诊断中
                updateStatus(diagnosis, STATUS_DIAGNOSING);
            } else if (STATUS_REFUSED.equals(diagnosis.getStatus())) {
                // 如果是拒绝状态，则更新为已拒绝
                updateStatus(diagnosis, STATUS_REFUSED);
            } else if (STATUS_COMPLETED.equals(diagnosis.getStatus())) {
                // 如果是完成状态，则更新为已完成
                updateStatus(diagnosis, STATUS_COMPLETED);
            }

            // 清除相关缓存
            clearCache(diagnosis.getId());
        } catch (Exception e) {
            log.error("Update diagnosis status failed", e);
            throw new RuntimeException("更新诊断状态失败", e);
        }
    }

    /**
     * 更新诊断状态的具体实现
     * 包括：
     * 1. 更新诊断日期和状态
     * 2. 更新关联的MRI图像状态
     * 3. 清理相关缓存
     *
     * @param diagnosis 诊断实体
     * @param status 目标状态
     * @throws RuntimeException 更新失败时抛出异常
     */
    private void updateStatus(Diagnosis diagnosis, String status) {
        try {
            // 设置诊断日期为当前时间
            diagnosis.setDiagnosisDate(String.valueOf(LocalDateTime.now()));

            // 更新诊断记录
            int result = diagnosisMapper.updateById(diagnosis);
            if (result <= 0) {
                log.error("Failed to update diagnosis with id: {}", diagnosis.getId());
                throw new RuntimeException("更新诊断记录失败");
            }

            // 更新关联的MRI图像状态
            MriImages mriImages = mriImagesMapper.selectById(diagnosis.getImageId());
            if (mriImages == null) {
                log.error("MRI image not found with id: {}", diagnosis.getImageId());
                throw new RuntimeException("未找到对应的 MRI 图像");
            }

            mriImages.setStatus(status);
            result = mriImagesMapper.updateById(mriImages);
            if (result <= 0) {
                log.error("Failed to update MRI image status with id: {}", diagnosis.getImageId());
                throw new RuntimeException("更新 MRI 图像状态失败");
            }

            // 清理相关缓存
            clearCache(diagnosis.getId());

        } catch (Exception e) {
            log.error("Failed to update status for diagnosis id: {}", diagnosis.getId(), e);
            throw new RuntimeException("更新状态失败: " + e.getMessage(), e);
        }
    }


    /**
     * 根据ID查询诊断记录
     * 采用缓存策略：优先从缓存获取，缓存未命中则查询数据库并更新缓存
     * @param id 诊断ID
     * @return 诊断实体
     */
    public Diagnosis selectById(Integer id) {
        String cacheKey = CACHE_KEY_ID + id;
        // 尝试从缓存获取
        Diagnosis diagnosis = redisCache.getCacheObject(cacheKey);
        if (diagnosis != null || redisCache.hasKey(cacheKey)) {
            return diagnosis;
        }

        // 缓存未命中，查询数据库
        diagnosis = diagnosisMapper.selectById(id);
        // 设置缓存，对空值设置较短的过期时间
        redisCache.setCacheObject(cacheKey, diagnosis,
                diagnosis == null ? CACHE_EXPIRE_SHORT_MINUTES : CACHE_EXPIRE_MINUTES,
                CACHE_EXPIRE_UNIT);
        return diagnosis;
    }

    /**
     * 条件查询诊断记录列表
     * 由于有查询条件，不使用缓存
     * @param diagnosis 查询条件
     * @return 诊断记录列表
     */
    public List<Diagnosis> selectAll(Diagnosis diagnosis) {
        return diagnosisMapper.selectAll(diagnosis);
    }

    /**
     * 用户视图分页查询
     * @param diagnosis 查询条件
     * @param pageNum 页码
     * @param pageSize 每页大小
     * @return 分页结果
     */
    public PageInfo<Diagnosis> selectDiagnosisPageToUser(Diagnosis diagnosis, Integer pageNum, Integer pageSize) {
        String cacheKey = buildUserPageCacheKey(diagnosis, pageNum, pageSize);

        // 尝试从缓存获取
        PageInfo<Diagnosis> pageInfo = redisCache.getCacheObject(cacheKey);
        if (pageInfo != null) {
            return pageInfo;
        }

        // 缓存未命中，查询数据库
        PageHelper.startPage(pageNum, pageSize);
        List<Diagnosis> list = diagnosisMapper.selectDiagnosisPageToUser(diagnosis);
        pageInfo = new PageInfo<>(list);

        // 设置缓存
        redisCache.setCacheObject(cacheKey, pageInfo, CACHE_EXPIRE_MINUTES, CACHE_EXPIRE_UNIT);
        return pageInfo;
    }

    /**
     * 专家视图分页查询，未诊断的
     * @param diagnosis 查询条件
     * @param pageNum 页码
     * @param pageSize 每页大小
     * @return 分页结果
     */
    public PageInfo<Diagnosis> selectDiagnosisPageToExpert(Diagnosis diagnosis, Integer pageNum, Integer pageSize) {
        String cacheKey = buildExpertPageCacheKey(diagnosis, pageNum, pageSize, "Diagnosis");

        // 尝试从缓存获取
        PageInfo<Diagnosis> pageInfo = redisCache.getCacheObject(cacheKey);
        if (pageInfo != null) {
            return pageInfo;
        }

        // 缓存未命中，查询数据库
        PageHelper.startPage(pageNum, pageSize);
        List<Diagnosis> list = diagnosisMapper.selectDiagnosisPageToExpert(diagnosis);
        pageInfo = new PageInfo<>(list);

        // 设置缓存
        redisCache.setCacheObject(cacheKey, pageInfo, CACHE_EXPIRE_MINUTES, CACHE_EXPIRE_UNIT);
        return pageInfo;
    }

    /**
     * 专家视图分页查询已诊断的记录
     * @param diagnosis
     * @param pageNum
     * @param pageSize
     * @return
     */
    public PageInfo<Diagnosis> selectDiagnosedPageToExpert(Diagnosis diagnosis, Integer pageNum, Integer pageSize) {
        String cacheKey = buildExpertPageCacheKey(diagnosis, pageNum, pageSize, "Diagnosed");

        // 尝试从缓存获取
        PageInfo<Diagnosis> pageInfo = redisCache.getCacheObject(cacheKey);
        if (pageInfo != null) {
            return pageInfo;
        }

        // 缓存未命中，查询数据库
        PageHelper.startPage(pageNum, pageSize);
        List<Diagnosis> list = diagnosisMapper.selectDiagnosedPageToExpert(diagnosis);
        pageInfo = new PageInfo<>(list);

        // 设置缓存
        redisCache.setCacheObject(cacheKey, pageInfo, CACHE_EXPIRE_MINUTES, CACHE_EXPIRE_UNIT);
        return pageInfo;
    }

    /**
     * 构建用户视图分页缓存键
     * 根据查询条件构建唯一的缓存键
     */
    private String buildUserPageCacheKey(Diagnosis diagnosis, Integer pageNum, Integer pageSize) {
        StringBuilder keyBuilder = new StringBuilder(CACHE_KEY_USER)
                .append("page:").append(pageNum)
                .append(":size:").append(pageSize)
                .append(":userId:").append(diagnosis.getUserId());

        if (StringUtils.hasText(diagnosis.getImageName())) {
            keyBuilder.append(":imageName:").append(diagnosis.getImageName());
        }
        return keyBuilder.toString();
    }

    /**
     * 构建专家视图分页缓存键
     * 根据查询条件构建唯一的缓存键
     */
    private String buildExpertPageCacheKey(Diagnosis diagnosis, Integer pageNum, Integer pageSize, String type) {
        StringBuilder keyBuilder = new StringBuilder(CACHE_KEY_EXPERT)
                .append("page:").append(pageNum)
                .append(":size:").append(pageSize)
                .append(":expertId:").append(diagnosis.getExpertId());

        if (StringUtils.hasText(diagnosis.getImageName())) {
            keyBuilder.append(":imageName:").append(diagnosis.getImageName());
        }
        if (StringUtils.hasText(diagnosis.getName())) {
            keyBuilder.append(":name:").append(diagnosis.getName());
        }
        keyBuilder.append(":type:").append(type);
        return keyBuilder.toString();
    }

    /**
     * 清除相关缓存
     * 当数据发生变化时，清除所有相关的缓存
     * @param id 诊断ID
     */
    private void clearCache(Integer id) {
        // 清除单个记录缓存
        redisCache.deleteObject(CACHE_KEY_ID + id);
        // 清除列表缓存
        redisCache.deleteObject(CACHE_KEY_ALL);

        // 清除用户视图和专家视图相关的所有缓存
        Set<String> userKeys = (Set<String>) redisCache.keys(CACHE_KEY_USER + "*");
        Set<String> expertKeys = (Set<String>) redisCache.keys(CACHE_KEY_EXPERT + "*");

        if (userKeys != null) {
            redisCache.deleteObject(userKeys);
        }
        if (expertKeys != null) {
            redisCache.deleteObject(expertKeys);
        }
    }

    /**
     * 管理员视图分页查询待审批的记录
     * @param diagnosis
     * @param pageNum
     * @param pageSize
     * @return
     */
    public PageInfo<Diagnosis> selectDiagnosisPendingToAdmin(Diagnosis diagnosis, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<Diagnosis> list = diagnosisMapper.selectAll(diagnosis);
        return PageInfo.of(list);
    }


}
